﻿using System;
using System.Collections.Generic;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel
{
    /// <summary>
    /// An undoable command for removing measures from a composition.
    /// </summary>
    public class RemoveMeasuresCommand : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoveMeasuresCommand"/> class.
        /// </summary>
        /// <param name="undoStack"></param>
        /// <param name="redoStack"></param>
        public RemoveMeasuresCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<Elements.Composition>("Composition", ParameterDirection.In);
            AddParameter<int>("Count", ParameterDirection.In);
            AddParameter<ushort>("Start", ParameterDirection.In);

            Name = "Remove Measures";
            removedMeasures = new List<Model.Elements.MeasureElement>();
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            var modelComposition = Composition.AdaptedElement as Model.Elements.CompositionElement;

            if (Composition.LinearView == null)
                throw new InvalidOperationException();

            for (int i = Start + Count - 2; i >= Start - 1; --i)
            {
                removedMeasures.Add(modelComposition.Measures[i]);
                modelComposition.Measures.RemoveAt(i);
            }

            Model.Elements.CWNotation.Layout.Layouter.LayoutHorizontallyNA(
                ((Elements.CWNotation.MusicSectionInterior)Composition.LinearView.Content.Interior).Systems[0].AdaptedElement as Model.Elements.CWNotation.System
                );

            Composition.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            var modelComposition = Composition.AdaptedElement as Model.Elements.CompositionElement;

            for (int i = Start + Count - 2; i >= Start - 1; --i)
            {
                removedMeasures.Add(modelComposition.Measures[i]);
                modelComposition.Measures.RemoveAt(i);
            }

            Model.Elements.CWNotation.Layout.Layouter.LayoutHorizontallyNA(
                ((Elements.CWNotation.MusicSectionInterior)Composition.LinearView.Content.Interior).Systems[0].AdaptedElement as Model.Elements.CWNotation.System
                );

            Composition.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            var modelComposition = Composition.AdaptedElement as Model.Elements.CompositionElement;

            for (int i = Start - 1; i < Start + Count - 1; ++i)
                modelComposition.Measures.Insert(i, removedMeasures[i - Start + 1]);

            Model.Elements.CWNotation.Layout.Layouter.LayoutHorizontallyNA(
                ((Elements.CWNotation.MusicSectionInterior)Composition.LinearView.Content.Interior).Systems[0].AdaptedElement as Model.Elements.CWNotation.System
                );

            Composition.FindOwner<Elements.Project>().IsModified = true;
        }

        #endregion

        #region Public Properties
        
        /// <summary>
        /// Gets or sets the composition from which the measures will be removed.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.Composition"/>
        /// </para>
        /// <para>
        /// The composition from which the measures will be removed.<br/>
        /// This property wraps the "Composition" command parameter.
        /// </para>
        /// </value>
        public Elements.Composition Composition
        {
            get { return GetParameterValue<Elements.Composition>("Composition"); }
            set { SetParameterValue<Elements.Composition>("Composition", value); }
        }

        /// <summary>
        /// Gets or sets the count of deleted measures.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Int32"/>
        /// </para>
        /// <para>
        /// The count of deleted measures.<br/>
        /// This property wraps the "Count" command parameter.
        /// </para>
        /// </value>
        public int Count
        {
            get { return GetParameterValue<int>("Count"); }
            set { SetParameterValue<int>("Count", value); }
        }

        /// <summary>
        /// Gets or sets the number of the first removed measure.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The number (1-based index) of the first removed measure.<br/>
        /// This property wraps the "Start" command parameter.
        /// </para>
        /// </value>
        public ushort Start
        {
            get { return GetParameterValue<ushort>("Start"); }
            set { SetParameterValue<ushort>("Start", value); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the collection of removed measures.
        /// </summary>
        private List<Model.Elements.MeasureElement> removedMeasures;

        #endregion
    }
}
